/*
 * dscan.c
 * KJ's simplified stream archiver (kjssar)
 * 
 * (C)Copyright 2013-2015 by Klaus-J. Wolf, kj (at) seismic (dot) de
 * This file is licensed under the General Public License Version 2.
 * http://www.gnu.org/licenses/gpl-2.0.html
 * 
 * This is a proof-of-concept implementation.
 * 
 */


#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "prelim.h"
#include "dscan.h"
#include "xat.h"


#define BUFSIZE 0x1000


struct kjssar_dentry *kjssar_dentry_scan_f(const uchar *root, int recurse)
{
    DIR *d;
    struct dirent *de;
    struct kjssar_dentry *res = NULL, *r;
    uchar *ffn;
    struct stat st;
    int i;
    
    d=opendir(root);
    IOASSERT(d,NULL)
    for(de=readdir(d); de; de=readdir(d))
    {
        ffn = concatfn_f(root,de->d_name);
        if(res==NULL)
            r = res = malloc(sizeof(struct kjssar_dentry));
        else
            r = r->nxt = malloc(sizeof(struct kjssar_dentry));
        MEMASSERT(r,NULL)
        i = lstat(ffn, &st);
        IOASSERT(i,NULL)
        
        r->ename = ffn;
        r->i_dev = st.st_dev;
        r->i_ino = st.st_ino;
        r->i_mode = st.st_mode;
        r->i_nlink = st.st_nlink;
        r->i_uid = st.st_uid;
        r->i_gid = st.st_gid;
        r->i_rdev = st.st_rdev;
        r->i_size = st.st_size;
        r->i_blksize = st.st_blksize;
        r->i_blocks = st.st_blocks;
        
        r->i_atime = st.st_atime;
        r->i_atime2 = 0;
        r->i_mtime = st.st_mtime;
        r->i_mtime2 = 0;
        r->i_ctime = st.st_ctime;
        r->i_ctime2 = 0;
        
        r->i_creatime = 0;
        r->i_creatime2 = 0;
        
#if USE_ACL
        r->x_acl = kjssar_getacl(ffn);
#else
        r->x_acl = NULL;
#endif
#if USE_XATTR
        r->x_xattr = kjssar_getxattr(ffn);
#else
        r->x_xattr = NULL;
#endif
        
        r->nxt = NULL;
        r->content = NULL;
        /* TODO: implement BSD, newer Linux features */
    }
    closedir(d);
    
    if(--recurse>0)
        for(r=res; r; r=r->nxt)
        {
            if(S_ISDIR(r->i_mode))
            {
                r->content = kjssar_dscan(r->ename,recurse);
            }
        }
    return res;
}


void kjssar_dentry_destroy(struct kjssar_dentry *r0)
{
    struct kjssar_dentry *r, *r1;
    
    for(r=r0; r; r=r1)
    {
        r1 = r->nxt;
        
        if(r->content)
            kjssar_dentry_destroy(r->content);
        free(r->ename);
        free(r);
    }
}


int kjssar_dentry_write(int fh, struct kjssar_dentry *single)
{
    int fh2 = dup(fh);
    FILE *f;
    char ty;
    
    IOASSERT(fh2,-1)
    f = fdopen(fh2, "w");
    IOXASSERT(f,NULL,-1)
    
    if(S_ISDIR(single->i_mode))
        ty = 'd';
    else if(S_ISCHR(single->i_mode))
        ty = 'c';
    else if(S_ISBLK(single->i_mode))
        ty = 'b';
    else if(S_ISREG(single->i_mode))
        ty = 'r';
    else if(S_ISFIFO(single->i_mode))
        ty = 'f';
    else if(S_ISLNK(single->i_mode))
        ty = 'n';
    else if(S_ISSOCK(single->i_mode))
        ty = 's';

    /* Attention!  The following code is fragile, but it's difficult to
     * determine in advance if it would work fully.  Therefore, we cast some
     * values so they at least won't break the stack.
     */
    fprintf(f, "\\EBEG\n")
    fprintf(f, "CD:%s\n", getsystemencoding());
    fprintf(f, "FN:%s\n", escctl(single->fn));   /* use local encoding here, unchanged */
    fprintf(f, "MODE:0o%5o\n", single->i_mode);
    fprintf(f, "UID:%s\n", uidstr(single->i_uid));
    fprintf(f, "GID:%s\n", gidstr(single->i_gid));
    fprintf(f, "SIZE:%llu\n", (long long unsigned) single->i_size);
    fprintf(f, "MTIME:%llu\n", (long long unsigned) single->i_mtime);
    fprintf(f, "CTIME:%llu\n", (long long unsigned) single->i_ctime);
    
    fprintf(f, "XATTR:%s\n", single->xattr);
    fprintf(f, "ACL:%s\n", single->acl);
    
    if(ty=='b' || ty=='c')
    {
        fputc(ty, c);  /* prefix with device type, so we get CDEV/BDEV */
        fprintf(f, "DEV:%d,%d\n", single->i_rdev_maj, single->i_rdev_min);
    }
    else if(single->ty=='n')
    {
        fprintf(f, "SYMLINK:%s\n", escctl(single->lnk));   /* use local encoding here, unchanged */
    }
    fprintf(f, "\\EEND\n")
    fclose(f);
}


int kjssar_dentry_read(int fh, struct kjssar_dentry *single)
{
    int fh2 = dup(fh);
    FILE *f;
    char linbuf[BUFSIZE];
    char buf[BUFSIZE];
    
    IOASSERT(fh2,-1)
    f = fdopen(fh2, "r");
    IOXASSERT(f,NULL,-1)
    setvbuf(f, linbuf, _IOLBF, BUFSIZE);
    
    if(!fgets(buf, BUFSIZE-1, f) || strcmp(buf, "\\EBEG\n"))
        return 1;
    
    while(fgets(buf, BUFSIZE-1, f))
    {
        if(!strcmp(buf, "\\EEND\n"))
            break;
        if(!strncmp(buf, "CD:"))
    }
    fclose(f);
    return 0;
}

